/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "../asn1/cmscbb_asn1_utils.h"
#include "cmscbb_err_def.h"
#include "cmscbb_sdk.h"
#include "../cmscbb_common/cmscbb_common.h"

/*
 * Prototype    : CmscbbFindOid
 * Description  : Find object id by id index.
 * Params
 *   [IN] aoid: id index
 * Return Value : matched object id, or CVB_NULL if not found.
 *   Date              Author     Modification
 *   2015/07/17 11:39  tangzonglei  Create
 */
const CmscbbAsnOid* CmscbbFindOid(CmscbbAoids aoid)
{
    CVB_INT i;
    CVB_INT cvbOidCount = (CVB_INT)GetCvbOidCount();
    CmscbbOidInfo* cvbOIDTable = GetCvbOIDTable();

    for (i = 0; i < cvbOidCount; ++i) {
        if (cvbOIDTable[i].aoid == aoid) {
            return &cvbOIDTable[i].asnOid;
        }
    }

    return CVB_NULL;
}

/*
 * Prototype    : CmscbbFindAoid
 * Description  : find id index by object id
 * Params
 *   [IN] oid: object id
 * Return Value : matched id index, or AOID_UNKNOWN if not found.
 *   Date              Author     Modification
 *   2015/07/29 17:20  tangzonglei  Create
 */
CmscbbAoids CmscbbFindAoid(const CmscbbAsnOid* oid)
{
    CVB_INT iter;
    CVB_INT cvbOidCount = (CVB_INT)GetCvbOidCount();
    CmscbbOidInfo* cvbOIDTable = GetCvbOIDTable();
    if (oid == CVB_NULL) {
        return AOID_UNKNOWN;
    }

    for (iter = 0; iter < cvbOidCount; ++iter) {
        if (CMSCBB_COMPARE_ASN_OID(oid, &(cvbOIDTable[iter].asnOid)) == 0) {
            return cvbOIDTable[iter].aoid;
        }
    }

    return AOID_UNKNOWN;
}

/*
 * Prototype    : CmscbbCompareAsnOcts
 * Description  : compare two CmscbbAsnOcts with octs
 *   Return Value : CVB_INT32
 *   if asnOctsExpect > asnOctsActual return >0
 *   else if asnOctsExpect == asnOctsActual return ==0
 *   else return <0
 *   Date              Author     Modification
 *   2015/08/03 9:35  tangzonglei  Create
 */
#define CVB_OCTS_COMPARE_FAILED (-1)
#define CVB_OCTS_COMPARE_EQUAL 0
CVB_INT32 CmscbbCompareAsnOcts(const CmscbbAsnOcts* asnOctsExpect, const CmscbbAsnOcts* asnOctsActual)
{
    if (asnOctsExpect == CVB_NULL || asnOctsActual == CVB_NULL) {
        return CVB_OCTS_COMPARE_FAILED;
    }

    if (asnOctsExpect == asnOctsActual) {
        return CVB_OCTS_COMPARE_EQUAL;
    }

    if (asnOctsExpect->len != asnOctsActual->len) {
        return CVB_OCTS_COMPARE_FAILED;
    }

    if (asnOctsExpect->octs == asnOctsActual->octs) {
        return CVB_OCTS_COMPARE_EQUAL;
    }

    return CmscbbMemCmp(asnOctsExpect->octs, asnOctsActual->octs, asnOctsActual->len);
}

/*
 * Prototype    : CmscbbConvertFromTime
 * Description  : Convert time object into time structure.
 * Params
 *   [IN] asnTime: a string of time in  asnTime->octs
 *   [OUT] signId: timestamp
 *   Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/08/17 14:30  tangzonglei  Create
 */
#define CVB_DECIMAL_BASE 10
#define GTC_YEAR_LENGTH 4
#define UTC_YEAR_LENGTH 2
#define GTC_YEAR_DECADE 2
#define GTC_YEAR_UNIT 3
#define MAX_UTC_YEAR 50
#define UTC_TIME_LENGTH 13
#define GTC_TIME_LENGTH 15
#define CVB_MONTH_HIGH 0
#define CVB_MONTH_LOW 1
#define CVB_DAY_HIGH 2
#define CVB_DAY_LOW 3
#define CVB_HOUR_HIGH 4
#define CVB_HOUR_LOW 5
#define CVB_MINU_HIGH 6
#define CVB_MINU_LOW 7
#define CVB_SECO_HIGH 8
#define CVB_SECO_LOW 9
CMSCBB_ERROR_CODE CmscbbConvertFromTime(const CmscbbAsnOcts* asnTime, CmscbbDatetime* signId)
{
    const CVB_BYTE* time = CVB_NULL;
    const CVB_UINT16 year2 = 2000;
    const CVB_UINT16 year = 1900;
    const CVB_UINT16 ten = 10;
    const CVB_UINT16 hunderd = 100;
    const CVB_UINT16 thousand = 1000;

    if (asnTime == CVB_NULL || signId == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    time = asnTime->octs;
    if (time == CVB_NULL) {
        CVB_LOG_ERROR(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    if (asnTime->tag.code == CBT_UTCTIME) {
        if (asnTime->len != UTC_TIME_LENGTH) {
            return CMSCBB_ERR_SYS_UTIL_CONVERT;
        }
        signId->uYear = (CVB_UINT16)((*time - '0') * CVB_DECIMAL_BASE + (*(time + 1) - '0'));
        signId->uYear = (CVB_UINT16)((CVB_INT)signId->uYear +
            (signId->uYear < MAX_UTC_YEAR ? year2 : year));

        time += UTC_YEAR_LENGTH;
    } else if (asnTime->tag.code == CBT_GENERALIZEDTIME) {
        if (asnTime->len != GTC_TIME_LENGTH) {
            return CMSCBB_ERR_SYS_UTIL_CONVERT;
        }
        signId->uYear = (CVB_UINT16)((*time - '0') * thousand + (*(time + 1) - '0') * hunderd +
            (*(time + GTC_YEAR_DECADE) - '0') * ten + (*(time + GTC_YEAR_UNIT) - '0'));
        time += GTC_YEAR_LENGTH;
    } else {
        return CMSCBB_ERR_SYS_UTIL_CONVERT;
    }

    signId->uMonth = (CVB_UINT8)((time[CVB_MONTH_HIGH] - '0') * CVB_DECIMAL_BASE + (time[CVB_MONTH_LOW] - '0'));
    signId->uDay = (CVB_UINT8)((time[CVB_DAY_HIGH] - '0') * CVB_DECIMAL_BASE + (time[CVB_DAY_LOW] - '0'));
    signId->uHour = (CVB_UINT8)((time[CVB_HOUR_HIGH] - '0') * CVB_DECIMAL_BASE + (time[CVB_HOUR_LOW] - '0'));
    signId->uMinute = (CVB_UINT8)((time[CVB_MINU_HIGH] - '0') * CVB_DECIMAL_BASE + (time[CVB_MINU_LOW] - '0'));
    signId->uSecond = (CVB_UINT8)((time[CVB_SECO_HIGH] - '0') * CVB_DECIMAL_BASE + (time[CVB_SECO_LOW] - '0'));

    return CVB_SUCCESS;
}

/*
 * Prototype    : CmscbbConvertDatetimeToTime
 * Description  : Convert time object to time_t value
 * Params
 *   [IN] asnTime: a string of time in  asnTime->octs
 *   [OUT] rTm: a timestamp
 * Return Value : CMSCBB_ERROR_CODE
 *
 *   Date              Author     Modification
 *   2015/08/17 14:41  tangzonglei  Create
 */
CMSCBB_ERROR_CODE CmscbbConvertDatetimeToTime(const CmscbbAsnOcts* asnTime, CVB_TIME_T* time)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbDatetime dt = {0};

    if (asnTime == CVB_NULL || time == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbConvertFromTime(asnTime, &dt);
    if (CVB_FAILED(ret)) {
        return ret;
    }

    *time = CmscbbMktime(&dt);
    return CVB_SUCCESS;
}

/*
 * Prototype    : CmscbbGetHashAoidFromSign
 * Description  : return associated hash algorithm from signature algorithm
 * Params
 *   [IN] signId: object id
 * Return Value : CmscbbAoids
 * Remarks      : Associated hash algorithm, or AOIDS_UNKNOW if not found.
 *   Date              Author     Modification
 *   2015/11/11 9:47  tangzonglei  Create
 */
CmscbbAoids CmscbbGetHashAoidFromSign(const CmscbbAsnOid* signId)
{
    CmscbbAoids hashAlgo = CmscbbFindAoid(signId);
    if (hashAlgo == AOID_SHA256 || hashAlgo == AOID_SHA256WITHRSAENCRYPTION || hashAlgo == AOID_SHA256WITHECDSA) {
        return AOID_SHA256;
    }
#if CMSCBB_SUPPORT_SHA384
    if (hashAlgo == AOID_SHA384 || hashAlgo == AOID_SHA384WITHRSAENCRYPTION || hashAlgo == AOID_SHA384WITHECDSA) {
        return AOID_SHA384;
    }
#endif
#if CMSCBB_SUPPORT_SHA512
    if (hashAlgo == AOID_SHA512 || hashAlgo == AOID_SHA512WITHRSAENCRYPTION || hashAlgo == AOID_SHA512WITHECDSA) {
        return AOID_SHA512;
    }
#endif

    return AOID_UNKNOWN;
}

/*
 * Prototype    : CmscbbGetEncAoidFromSign
 * Description  : return associated hash algorithm from signature algorithm
 * Params
 *   [IN] signId: object id
 * Return Value : CMSCBB_AOIDS
 * Remarks      : Associated hash algorithm, or AOIDS_UNKNOW if not found.
 */
CmscbbAoids CmscbbGetEncAoidFromSign(const CmscbbAsnOid* signId)
{
    CmscbbAoids encAlgo = CmscbbFindAoid(signId);
    if (encAlgo == AOID_SHA256WITHRSAENCRYPTION || encAlgo == AOID_RSA) {
        return AOID_RSA;
    }
    if (encAlgo == AOID_ECDSA  || encAlgo == AOID_SHA256WITHECDSA) {
        return AOID_ECDSA;
    }
#if CMSCBB_SUPPORT_SHA384
    if (encAlgo == AOID_SHA384WITHRSAENCRYPTION) {
        return AOID_RSA;
    }
    if (encAlgo == AOID_SHA384WITHECDSA) {
        return AOID_ECDSA;
    }
#endif
#if CMSCBB_SUPPORT_SHA512
    if (encAlgo == AOID_SHA512WITHRSAENCRYPTION) {
        return AOID_RSA;
    }
    if (encAlgo == AOID_SHA512WITHECDSA) {
        return AOID_ECDSA;
    }
#endif
    if (encAlgo == AOID_RSASSAPSS) {
        return AOID_RSASSAPSS;
    }
    return AOID_UNKNOWN;
}

